Skip to content

Conversation

DerGuteMoritz
Copy link
Collaborator

@DerGuteMoritz DerGuteMoritz commented Sep 19, 2025

The result deferred d may end up in an error state when (f) throws an exception or returns an error deferred. Now since the cancellation callback was only attached via d/chain for side-effect (i.e. the resulting deferred is not returned), the dropped error detection would be triggered in this case. The fix is simply to also attach an error handler with d/on-realized.

Furthermore, putting the result deferred in an error state would not have cancelled the timer because there was no error handler attached to it. This is now also fixed and the tests are extended to cover the cancellation API, too.

The latter uncovered an oversight in the mock clock implementation where the in method didn't return a cancellation function, resulting in an NPE. This is now also fixed.

Based on #245.

@DerGuteMoritz DerGuteMoritz changed the title Fix dropped errors in manifold.time/in Fix dropped errors in manifold.time/in Sep 19, 2025
The result deferred `d` may end up in an error state when `(f)` throws an exception or returns an
error deferred. Now since the cancellation callback was only attached via `d/chain` for
side-effect (i.e. the resulting deferred is not returned), the dropped error detection would be
triggered in this case. The fix is simply to also attach an error handler with `d/on-realized`.

Furthermore, putting the result deferred in an error state would not have cancelled the timer
because there was no error handler attached to it. This is now also fixed and the tests are extended
to cover the cancellation API, too.

The latter uncovered an oversight in the mock clock implementation where the `in` method didn't
return a cancellation function, resulting in an NPE. This is now also fixed.
@DerGuteMoritz DerGuteMoritz force-pushed the fix-dropped-errors-in-manifold-time branch from febc716 to 69e53a7 Compare September 19, 2025 14:28
(in [this interval-millis f]
(swap! events update-in [(p/+ ^double @now interval-millis)] #(conj (or % []) f))
(advance this 0))
(let [t (p/+ ^double @now interval-millis)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be a swap! ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On now you mean? If so: no, the current clock time can only be changed via advance for the mock clock. Also note that this was already the case before - I merely pulled out the key construction from the update-in so that I can re-use it in cancel-fn.

(update t disj f))))]
(swap! events update t (fnil conj #{}) f)
(advance this 0)
cancel-fn))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this changes the return value from nil (what advance returns) to a fn.

is that ok?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not only OK but fixes the mock clock implementation to conform to the expected return value. Quoting from the commit message / PR description:

The latter uncovered an oversight in the mock clock implementation where the in method didn't return a cancellation function, resulting in an NPE. This is now also fixed.

Compare with the real clock implementation and here for where this would fail if we didn't return a function here.

(swap! events #(cond-> %
(contains? % t)
(update t disj f))))]
(swap! events update t (fnil conj #{}) f)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could there be a race condition between the deref now on L194 and this swap?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only if there is a concurrent call to advance (which would be an odd thing to do in a test 😅). Even if, it wouldn't be an issue. It would only be one if we dereferenced now multiple times here which is what I prevented by pulling out the key calculation and save it in t for re-use. Makes sense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants